//Copyright (c) 2008, William Severance, Jr., WESNet Designs
//All rights reserved.

Type.registerNamespace('WESNet.AJAX.Controls');

// Portions of ControlBase Script were extracted from AjaxControlToolkit

WESNet.AJAX.Controls.ControlBase = function(element) {
    WESNet.AJAX.Controls.ControlBase.initializeBase(this, [element]);
    this._clientStateField = null;
    this._callbackTarget = null;
    this._onsubmit$delegate = Function.createDelegate(this, this._onsubmit);
    this._oncomplete$delegate = Function.createDelegate(this, this._oncomplete);
    this._onerror$delegate = Function.createDelegate(this, this._onerror);
}
WESNet.AJAX.Controls.ControlBase.prototype = {
    initialize : function() {
        WESNet.AJAX.Controls.ControlBase.callBaseMethod(this, "initialize");
        // load the client state if possible
        if (this._clientStateField) {
            this.loadClientState(this._clientStateField.value);
        }
        // attach an event to save the client state before a postback or updatepanel partial postback
        if (typeof(Sys.WebForms)!=="undefined" && typeof(Sys.WebForms.PageRequestManager)!=="undefined") {
            Array.add(Sys.WebForms.PageRequestManager.getInstance()._onSubmitStatements, this._onsubmit$delegate);
        } else {
            $addHandler(document.forms[0], "submit", this._onsubmit$delegate);
        }
    },
    dispose : function() {
        if (typeof(Sys.WebForms)!=="undefined" && typeof(Sys.WebForms.PageRequestManager)!=="undefined") {
            Array.remove(Sys.WebForms.PageRequestManager.getInstance()._onSubmitStatements, this._onsubmit$delegate);
        } else {
            $removeHandler(document.forms[0], "submit", this._onsubmit$delegate);
        }
        WESNet.AJAX.Controls.ControlBase.callBaseMethod(this, "dispose");
    },
    findElement : function(id) {
        // <summary>Finds an element within this control (ScriptControl/ScriptUserControl are NamingContainers);
        return $get(this.get_id() + '_' + id.split(':').join('_'));
    },
    get_clientStateField : function() {
        return this._clientStateField;
    },
    set_clientStateField : function(value) {
        if (this.get_isInitialized()) throw Error.invalidOperation('Cannot set ClientStateField');
        if (this._clientStateField != value) {
            this._clientStateField = value;
            this.raisePropertyChanged('clientStateField');
        }
    },
    loadClientState : function(value) {
        /// <remarks>override this method to intercept client state loading after a callback</remarks>
    },
    saveClientState : function() {
        /// <remarks>override this method to intercept client state acquisition before a callback</remarks>
        return null;
    },
    _invoke : function(name, args, cb) {
        /// <summary>invokes a callback method on the server control</summary>        
        if (!this._callbackTarget) {
            throw Error.invalidOperation('Control not registered for client callbacks');
        }
        if (typeof(WebForm_DoCallback)==="undefined") {
            throw Error.invalidOperation('Page not registered for client callbacks');
        }
        var ar = [];
        for (var i = 0; i < args.length; i++) 
            ar[i] = args[i];
        var clientState = this.saveClientState();
        if (clientState != null && !String.isInstanceOfType(clientState)) {
            throw Error.invalidOperation('Invalid client state type');
        }
        var payload = Sys.Serialization.JavaScriptSerializer.serialize({name:name,args:ar,state:this.saveClientState()});
        WebForm_DoCallback(this._callbackTarget, payload, this._oncomplete$delegate, cb, this._onerror$delegate, true);
    },
    _oncomplete : function(result, context) {
        result = Sys.Serialization.JavaScriptSerializer.deserialize(result);
        if (result.error) {
            throw Error.create(result.error);
        }
        this.loadClientState(result.state);
        context(result.result);
    },
    _onerror : function(message, context) {
        throw Error.create(message);
    },
    _onsubmit : function() {
        if (this._clientStateField) {
            this._clientStateField.value = this.saveClientState();
        }
        return true;
    }    
   
}
WESNet.AJAX.Controls.ControlBase.registerClass("WESNet.AJAX.Controls.ControlBase", Sys.UI.Control);

// End of ControlBase Script

WESNet.AJAX.Controls.ImageEditorBehavior = function(element) {
    WESNet.AJAX.Controls.ImageEditorBehavior.initializeBase(this, [element]);
    
    // Constants
    this._thresholdX=1;
    this._thresholdY=1;
    
    // Properties
    this._callbackID = null;
    this._autoPostBack = false;
    this._cropRectangle = {X:0, Y:0, Width: 0, Height: 0};
    this._imageSize = {Width: 0, Height: 0};
    this._previewSize = {Width: 0, Height: 0};
    this._scale= {X: 1.00, Y: 1.00};
    this._minCropSize = {Width: 0, Height: 0};
    this._minWidth = 0;
    this._minHeight = 0;
    this._maxCropSize = {Width: 0, Height: 0};
    this._maxWidth = 0;
    this._maxHeight = 0;
    this._aspect = {X: 0, Y: 0};
    this._attachCropperOnLoad = true;
    this._showCropperOnInit = false;
    this._showStatusBar = true;
    this._showImageSize = true;
    this._captureKeys = false;
    this._prefix='WESNetImageEditor_';
    this._waitImageUrl = '';
    this._cacheKeyPrefix = 'IMG';
    this._clientState = null;
    
    //Variables
    this._id = '';
    this._captionBar = null;
    this._filenameBar = null;
    this._img = null;
    this._parentNode = null;
    this._imgW = 0;
    this._imgH = 0;
    this._lastX = 0;
    this._lastY = 0;
    this._lastAspect = null;
    this._attached = false;
    this._resizing = false;
    this._dragging = false;
    this._resizeHandle = null;
    this._frame = null;
    this._selectedArea = null;
    this._clickableArea = null;
    this._statusBar = null;
    
    this._dragArea = null;
    this._masks = [];
    this._handles = [];
    this._imageGuid = null;
    this._imagePath = null;
    this._animationArea = null;
    this._processStartTime = 0;
    this._processEndTime = 0;
    //this._useWebService = false;
        
    //Delegates
    this._onImageLoadDelegate = null;
    this._onMouseDownDelegate = null;
    this._onDragDelegate = null;
    this._onEndDragDelegate = null;
    this._onBeginResizeDelegate = null;
    this._onResizeDelegate = null;
    this._onEndResizeDelegate = null;
    this._onDblClickDelegate = null;
    this._onKeyDownDelegate= null;
}

WESNet.AJAX.Controls.ImageEditorBehavior.prototype = {

    // Initialization
    initialize : function() {
        WESNet.AJAX.Controls.ImageEditorBehavior.callBaseMethod(this, 'initialize');
        this._id = this.get_element().id;
        this._captionBar = $get(this._id + '_CaptionBar');
        this._filenameBar = $get(this._id + '_FilenameBar');
        this._img = $get(this.get_element().id + '_imgPreview');
        
        if (!this._img) {
             alert ('ImageEditor script control has not created an HtmlImage control as its preview/editing area.');
             return;
        }
        if (this._parentNode == null) this._parentNode = this._img.parentNode;
        if (this._showStatusBar) this.attachStatusBar();
      
        this._onImageLoadDelegate = Function.createDelegate(this, this.onImageLoad);
        $addHandler (this._img, 'load', this._onImageLoadDelegate);
        
        // just in case the image loaded before the handler was added. Should work for 
        // IE > 4 and FF. Uncertain about other browsers . . .
        
        if (this._img.complete) this.onImageLoad();  
     },
        
    onImageLoad: function () {
        if (this._img.src.indexOf('path=') != -1) {
             this._imagePath = this._img.src.match(/(?:path=)([^&]+)/)[1];
        }
        this._imageGuid = this._img.src.match(/(?:guid=IMG)([^&]+)/)[1];
        
        this._imgW = this._img.width;
        this._imgH = this._img.height;
        this._imageSize = this.get_ImageSize();
        this._scale.X = this._imageSize.Width/this._imgW;
        this._scale.Y = this._imageSize.Height/this._imgH;
               
        this.setMinMaxes();
                    
        if (this._attachCropperOnLoad && !this._attached) {
            this.attachCropper();
        }
        
        if (this._attached) {
            this._frame.style.width=this._imgW + 'px';
            this._frame.style.height=this._imgH + 'px';
            this._frame.style.top = Math.ceil ((this._parentNode.clientHeight - (this._statusBar ? this._statusBar.clientHeight : 0) - this._imgH)/2) + 'px';
            this._maskTop.style.width = this._frame.style.width;
            this._maskBottom.style.width = this._frame.style.width;
            this._clickableArea.style.width = this._frame.style.width;
            this._clickableArea.style.height = this._frame.style.height;
            if (this._statusBar) this.updateStatusBar();
        }
    },
    
    setupCropper: function () {
        var rect = this.get_CropRectangle();
        if (rect.X > this._imgW) rect.X = this._imgW;
        if (rect.Y > this._imgH) rect.Y = this._imgH;
        if ((rect.X + rect.Width) > this._imgW) rect.Width = this._imgW - rect.X;
        if ((rect.Y + rect.Height) > this._imgH) rect.Height = this._imgH - rect.Y;
        this._cropRectangle = rect;
        
        this._resizing=false;
        this._dragging=false;
        this._resizeHandle=null;
       
        if (this._showCropperOnInit) {
             var x = 0;
             var y = 0;       
             if ((this._cropRectangle.Width > 0) && (this._cropRectangle.Height > 0)) {
                 x = Math.ceil((this._imgW-this._cropRectangle.Width)/2);
                 y = Math.ceil((this._imgH-this._cropRectangle.Height)/2);
                 rect = {X: x, Y: y, Width: this._cropRectangle.Width, Height:this._cropRectangle.Height};
             } else if ((this._minWidth > 0) && (this._minHeight > 0)) {
                 x = Math.ceil((this._imgW - this._minWidth)/2);
                 y = Math.ceil((this._imgH-this._minHeight)/2);
                  rect = {X: x, Y: y, Width: this._minWidth, Height: this._minHeight};
             } else if ((this._aspect.X > 0) && (this._aspect.Y > 0)) {
                 var m = Math.min(this._imgW / this._aspect.X, this._imgH / this._aspect.Y);
                 x = Math.ceil(this._aspect.X * m * .2);
                 y = Math.ceil(this._aspect.Y * m * .2);
                 rect = {X: x, Y: y, Width: this._imgW - (x * 2), Height: this._imgH - (y * 2)};
             } else {
                  rect = {X: 0, Y: 0, Width: this._imgW, Height: this._imgH};
             }
             this.setCropRectangle(rect, false, false, false, null);
             this._selectedArea.style.visibility = 'visible';
             this._showCropperOnInit = false;
        } else {
             this.resetCropper();
             if (this._statusBar) this.updateStatusBar();
        }
    },
     
    attachStatusBar: function() {
        if (this._statusBar == null) {
             this._statusBar = this.buildElement('DIV', this._prefix + 'StatusBar');
            this._statusBar.innerHTML = '&nbsp;';
            this._statusBar.style.width='100%';
            this._parentNode.appendChild(this._statusBar);                    
        }   
    },
    
    removeStatusBar: function () {
        if (this._statusBar) {
           this._parentNode.removeChild (this._statusBar);
           this._statusBar = null;
        }
    },
    
    attachCropper: function() {
        if (!this._attached) {
            this._frame=this.buildElement('DIV', this._prefix + 'Wrapper');
            this._frame.appendChild (this._img);
            this._parentNode.insertBefore(this._frame, this._parentNode.firstChild);     
            this._maskTop=this.buildElement('DIV', this._prefix + 'Mask', this.buildElement('SPAN'));
            this._maskRight=this.buildElement('DIV', this._prefix + 'Mask', this.buildElement('SPAN'));
            this._maskBottom=this.buildElement('DIV', this._prefix + 'Mask', this.buildElement('SPAN'));
            this._maskLeft=this.buildElement('DIV', this._prefix + 'Mask', this.buildElement('SPAN'));
            this._masks=[this._maskTop, this._maskRight, this._maskBottom, this._maskLeft];     
        
            this._handleN = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleN');
            this._handleNE = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleNE');
            this._handleE = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleE');
            this._handleSE = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleSE');
            this._handleS = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleS');
            this._handleSW = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleSW');
            this._handleW = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleW');
            this._handleNW = this.buildElement('DIV', this._prefix + 'Handle ' + this._prefix + 'HandleNW');
            this._handles = [this._handleN, this._handleNE, this._handleE, this._handleSE,
                               this._handleS, this._handleSW, this._handleW, this._handleNW];
            this._selectedArea = this.buildElement('DIV', this._prefix + 'SelectedArea', this._handles);
            this._selectedArea.style.visibility = 'hidden';
            this._clickableArea = this.buildElement('DIV', this._prefix + 'ClickableArea')
            this._dragArea=this.buildElement('DIV', this._prefix + 'DragArea', this._masks, this._selectedArea, this._clickableArea);
            this._frame.appendChild (this._dragArea);
            this.registerHandles(true);
            this._onDblClickDelegate = Function.createDelegate (this, this.onDblClick);
            $addHandler (this._selectedArea, 'dblclick', this._onDblClickDelegate);
            if (this._captureKeys) {
                 this._onKeyDownDelegate = Function.createDelegate (this, this.onKeyDown);
                 $addHandler (document, 'keydown', this._onKeyDownDelegate);
            }
            this._attached=true;
            this._onMouseDownDelegate=Function.createDelegate(this, this.onMouseDown);
            $addHandler (this._dragArea, 'mousedown', this._onMouseDownDelegate);
            this.setupCropper();
       }
    },
    
    removeCropper: function() {
        if (this._attached) {
            if (this._onMouseDownDelegate) {
              $removeHandler (this._dragArea, 'mousedown', this._onMouseDownDelegate);
              this._onMouseDownDelegate = null;
            }
            if (this._onDblClickDelegate) {
              $removeHandler (this._selectedArea, 'dblclick', this._onDblClickDelegate);
              this._onDblClickDelegate = null;
            }
            
            if (this._onKeyDownDelegate) {
              $removeHandler(document, 'keydown', this._onKeyDownDelegate);
              this._onKeyDownDelegate = null;
            }
            this.registerHandles(false);
                       
            if (this._onDragDelegate) {
              $removeHandler (document, 'mousemove', this._onDragDelegate);
            }
            if (this._onEndDragDelegate) {
              $removeHandler (document, 'mouseup', this._onEndDragDelegate);
            }
            
            if (this._onResizeDelegate) {
              $removeHandler (document, 'mousemove', this._onResizeDelegate);
            }
            if (this._onEndResizeDelegate) {
              $removeHandler (document, 'mouseup', this._onEndResizeDelegate);
            }
            
            this._attachOnLoad = false;
            this._attached = false;
            this._showOnInit = false;
            
            this._parentNode.insertBefore (this._img, this._frame);
            this._parentNode.removeChild (this._frame);
            this._frame = null;
        }
    },
    
    setMinMaxes: function() {
        this._maxWidth = Math.ceil(this._maxCropSize.Width / this._scale.X);
        this._maxHeight = Math.ceil(this._maxCropSize.Height / this._scale.Y);
        if (this._maxWidth == 0 || this._maxWidth > this._imgW) {
              this._maxWidth = this._imgW;
        }
        if (this._maxHeight == 0 || this._maxHeight > this._imgH) {
              this._maxHeight = this._imgH;
        }
        
        this._minWidth = Math.ceil(this._minCropSize.Width / this._scale.X);
        this._minHeight = Math.ceil(this._minCropSize.Height / this._scale.Y);
        if (this._minWidth < 0) {
              this._minWidth = 0;
        }
        if (this._minHeight < 0) {
              this._minHeight = 0;
        }
        
        this._isFixedWidth = ((this._maxWidth>0) && (this._maxWidth == this._minWidth));
        this._isFixedHeight = ((this._maxHeight>0) && (this._maxHeight == this._minHeight));
    },
    
    resetCropper: function () {
             this._cropRectangle = {X: 0, Y: 0, Width: 0, Height: 0};
             this._maskTop.style.height = '0px';
             this._maskRight.style.width = '0px';
             this._maskRight.style.height = '0px';
             this._maskBottom.style.height = '0px';
             this._maskLeft.style.width = '0px';
             this._maskLeft.style.height = '0px';
             this._selectedArea.style.visibility = 'hidden';
    },
    
    // Cleanup Code
    dispose : function() {
        this.removeCropper();
        if (this._onMouseDownDelegate) {
          $removeHandler (this._dragArea, 'mousedown', this._onMouseDownDelegate);
        }
        if (this._onImageLoadDelegate) {
          $removeHandler (this._img, 'load', this._onImageLoadDelegate);
        }
        WESNet.AJAX.Controls.ImageEditorBehavior.callBaseMethod(this, 'dispose');
    },
    
     onMouseDown: function(e) {
        if (!e) {
           e = event;
        }
        var cumulativeOffset = this.getCumulativeOffset(this._frame);
        var x = e.clientX + document.body.scrollLeft - cumulativeOffset.X;
        var y = e.clientY + document.body.scrollTop - cumulativeOffset.Y;       
        var isHandle = (e.target.className.match(/Handle/) == 'Handle');
        if (this._selectedArea.style.visibility == 'hidden') {
             this._selectedArea.style.visibility = 'visible';
             var rect = {X: x, Y: y, Width: 0, Height: 0};  
             this.setCropRectangle(rect, false, false, false, {X: 1, Y: 1});                 
             this._lastX = e.clientX;
             this._lastY = e.clientY;
             this._resizing=true;
             this._resizeHandle='SE';
             this._onResizeDelegate=Function.createDelegate(this, this.onResize);
             $addHandler (document, 'mousemove', this._onResizeDelegate);
             this._onEndResizeDelegate=Function.createDelegate(this, this.onEndResize);
             $addHandler (document, 'mouseup', this._onEndResizeDelegate);
             e.target = this._handles[3];
             this.onResize (e);
        }
        else if (!isHandle) {
              var i = this.insideRect(x, y, this._cropRectangle);
              if (i < 0) {
                 var rect = {X: x, Y: y, Width: 0, Height:0};                    
                 this.setCropRectangle(rect, false, false, false, null);
                 this._lastX = e.clientX;
                 this._lastY = e.clientY;
                 this._resizing=true;
                 this._resizeHandle='SE';
                 this._onResizeDelegate=Function.createDelegate(this, this.onResize);
                 $addHandler (document, 'mousemove', this._onResizeDelegate);
                 this._onEndResizeDelegate=Function.createDelegate(this, this.onEndResize);
                 $addHandler (document, 'mouseup', this._onEndResizeDelegate);
              } else {
                 this._lastX = e.clientX;
                 this._lastY = e.clientY;
                 this._dragging = true;
                 this._resizing =false;
                 this._onDragDelegate=Function.createDelegate(this, this.onDrag);
                 $addHandler (document, 'mousemove', this._onDragDelegate);
                 this._onEndDragDelegate=Function.createDelegate(this, this.onEndDrag);
                 $addHandler (document, 'mouseup', this._onEndDragDelegate);
              }
        }
    },
    
    onDblClick: function(e) {
        if (!e) {
           e = event;
        }
        this.set_CropRectangle(null);
        this.resetCropper();
    },
    
    onKeyDown: function (e) {
        if (!e) {
            e = event;
        }
        var vector = {X: 0, Y: 0};
        if (this.isCropRectangleVisible() && !this._dragging && !this._resizing) {
             switch (e.keyCode) {
                 case (Sys.UI.Key.esc): // Esc - removes crop rectangle
                    this.resetCropper();
                    return false;
                 case (Sys.UI.Key.left): // left arrow
                    vector.X = -1;
                    break;
                 case (Sys.UI.Key.up): // up arrow
                    vector.Y = -1;
                    break;
                 case (Sys.UI.Key.right): // right arrow
                    vector.X = 1;
                    break;
                 case (Sys.UI.Key.down): // down arrow
                    vector.Y = 1;
                    break;
                 default:
                    return true;
             }
             //multiply movements by 10 when shift key is down
             if (e.shiftKey) {
                  vector.X *= 10; 
                  vector.Y *= 10;
             }
             this.setCropRectangle(this._cropRectangle, true, false, false, vector);
             return false;
       }
       return true;
    },
    
    onDrag: function(e) {
        if (!e) {
           e = event;
        }
        
        var vector = {X: e.clientX - this._lastX, Y: e.clientY - this._lastY};  
             
        if (this._dragging) {
           if ((Math.abs(vector.X)>=this._thresholdX) || (Math.abs(vector.Y)>=this._thresholdY)) {
                 this._lastX = e.clientX;
                 this._lastY = e.clientY;
                 this.setCropRectangle(this._cropRectangle, true, false, false, vector);      
           }
        }
    },
       
    onEndDrag: function(e) {
        if (!e) {
          e = event;
        }
        if (this._dragging) {
            if (this._onDragDelegate != null) {
               $removeHandler(document, 'mousemove', this._onDragDelegate);
               this._onDragDelegate = null;
            }
            if (this._onEndDragDelegate !=null) {
               $removeHandler (document, 'mouseup', this._onEndDragDelegate);
               this._onEndDragDelegate = null;
            }
            this._dragging=false;
        }
    },
    
    onBeginResize: function (e) {
        if (!e) {
           e = event;
        }
           var handle = e.target;
           this._lastX = e.clientX;
           this._lastY = e.clientY;
           if (handle.className.match(/Handle/) == 'Handle') {     
              this._resizing=true;
              this._resizeHandle=handle.className.match(/(?:Handle)(([N|S]([E|W])?)|([E|W]))$/)[1];
              this._onResizeDelegate=Function.createDelegate(this, this.onResize);
              $addHandler (document, 'mousemove', this._onResizeDelegate);
              this._onEndResizeDelegate=Function.createDelegate(this, this.onEndResize);
              $addHandler (document, 'mouseup', this._onEndResizeDelegate);
           }
    },
    
    onResize: function (e) {
        if (!e) {
           e = event;
        }
        if (this._resizing) {
          var vector = {X: e.clientX - this._lastX, Y: e.clientY - this._lastY}; 
          if ((Math.abs(vector.X)>=this._thresholdX) || (Math.abs(vector.Y)>=this._thresholdY)) {
             this._lastX = e.clientX;
             this._lastY = e.clientY;
             this.setCropRectangle(this._cropRectangle, false, e.shiftKey, e.ctrlKey, vector);
          }
        }
    },
    
    onEndResize: function (e) {
        if (!e) {
           e = event;
        }
        
        if (this._resizing) {
            if (this._onResizeDelegate !=null) {
                 $removeHandler(document, 'mousemove', this._onResizeDelegate);
                 this._onResizeDelegate = null;
            }
            if (this._onEndResizeDelegate !=null) {
                 $removeHandler (document, 'mouseup', this._onEndResizeDelegate);
                 this._onEndResizeDelegate = null;
            }
            this._resizing=false;
            this._resizeHandle=null;
        }
    },
    
    isCropRectangleVisible: function() {
        if (this._attached) return this._selectedArea.style.visibility != 'hidden';
        else return false;
    },               
        
    getCumulativeOffset: function(el) {
//        var loc = Sys.UI.DomElement.getLocation(el);      
//        return {X: loc.x, Y: loc.y};
//        Although the above MS AJAX method should work in most browsers, it does not take into account border widths
//        Prior to FF 3.0, Mozilla browsers did not support the clientTop and clientLeft properties so
//        we attempt to approximate this by offsetHeight-clientHeight or offsetWidth-clientWidth on the
//        questionable assumption that the borders are the same with on all sides of an element. Can't
//        figure out why we don't need to divide this by 2 however ????
        var offsetsX = 0, offsetsY = 0;
        do {
          offsetsY += ((el.offsetTop || 0) + (el.clientTop || (el.offsetHeight-el.clientHeight)));
          offsetsX += ((el.offsetLeft || 0) + (el.clientLeft || (el.offsetWidth-el.clientWidth)));
          el = el.offsetParent;
        } while (el);
        return {X: offsetsX, Y: offsetsY};
    },
    
    insideRect: function (X, Y, rect) {
        if (rect) {
           if (((X > rect.X) && (X < (rect.X + rect.Width-1))) && ((Y > rect.Y) && (Y < (rect.Y + rect.Height-1)))) {
               return 1;
           } 
           
           if (((X < rect.X) || (X > (rect.X + rect.Width-1))) || ((Y < rect.Y) || (Y > (rect.Y + rect.Height-1)))) {
               return -1;
           } 
           return 0;
        }
        return -1;
    }, 
    
    setCropRectangle: function (r, dragging, square, maintain, vector) {
        var x, y, w, wc, h, hc;
        var rect = this.cloneRectangle (r);
        if (dragging) {  
           if (vector !=null) {
                rect.X = rect.X + vector.X;
                rect.Y = rect.Y + vector.Y;
           }
           w = rect.Width;
           h = rect.Height;
           if (rect.X < 0) rect.X = 0;
           if (rect.Y < 0) rect.Y = 0;
           if ((rect.X + w) >= this._imgW) rect.X=this._imgW-w;
           if ((rect.Y + h) >= this._imgH) rect.Y=this._imgH-h;
        }
        else {
           if (this._resizeHandle !=null && vector !=null) {
                if (this._resizeHandle.endsWith('W')) {
                      x = Math.min(Math.max(rect.X + vector.X, 0), this._imgW);
                      var deltaX = x - rect.X;
                      if (deltaX != 0) {
                          w = rect.Width - (deltaX);
                          if (this._minWidth > 0) {
                             wc = Math.min(Math.max(w, this._minWidth), this._maxWidth);
                             x = x - (wc-w);
                          } else {
                              flippedDirection = (w < 0);
                              wc = Math.min(Math.abs(w), this._maxWidth);
                              x = x - (wc-Math.abs(w));
                              if (flippedDirection) {
                                    x = x - wc;
                                    this._resizeHandle=this._resizeHandle.replace('W', 'E');
                              }
                          }
                          rect.X = x;
                          rect.Width = wc;
                      } 
                } else if (this._resizeHandle.endsWith('E')) {
                     w = Math.min(rect.Width + vector.X, Math.min(this._maxWidth, this._imgW-rect.X));
                     var deltaW = w-rect.Width;
                     if (deltaW != 0) {
                         if (this._minWidth > 0) {
                              w = Math.max(w, this._minWidth);
                         } else if (w<0) {
                             rect.X = Math.max(rect.X + w, 0);
                             w = Math.min(Math.abs(w), Math.min(rect.X, this._maxWidth));
                             this._resizeHandle=this._resizeHandle.replace('E', 'W');
                         }
                         rect.Width = w;
                     }
               }   
                                
               if (this._resizeHandle.startsWith('N')) {
                      y = Math.min(Math.max(rect.Y + vector.Y, 0), this._imgH);
                      var deltaY = y - rect.Y;
                      if (deltaY != 0) {
                          h = rect.Height - (deltaY);
                          if (this._minHeight > 0) {
                             hc = Math.min(Math.max(h, this._minHeight), this._maxHeight);
                             y = y - (hc-h);
                          } else {
                              flippedDirection = (h < 0);
                              hc = Math.min(Math.abs(h), this._maxHeight);
                              y = y - (hc-Math.abs(h));
                              if (flippedDirection) {
                                    y = y - hc;
                                    this._resizeHandle=this._resizeHandle.replace('N', 'S');
                              }
                          }
                          rect.Y = y;
                          rect.Height = hc;
                      } 
                } else if (this._resizeHandle.startsWith('S')) {
                     h = Math.min(rect.Height + vector.Y, Math.min(this._maxHeight, this._imgH-rect.Y));
                     var deltaH = h-rect.Height;
                     if (deltaH != 0) {
                         if (this._minHeight > 0) {
                              h = Math.max(h, this._minHeight);
                         } else if (h<0) {
                             rect.Y = Math.max(rect.Y + h, 0);
                             h = Math.min(Math.abs(h), Math.min(rect.Y, this._maxHeight));
                             this._resizeHandle=this._resizeHandle.replace('S', 'N');
                         }
                         rect.Height = h;
                     }
                }
              }
                 
           var ratio;  
           if (square) {
                ratio = {X: 1, Y: 1};
           } else if (maintain) {
                if (this._lastAspect == null) {
                     var GCD = this.calcGCD(r.Width, r.Height);
                     this._lastAspect = {X: r.Width / GCD, Y: r.Height / GCD};   
                }
                ratio = this._lastAspect;
           } else {
              ratio = this._aspect;
              this._lastAspect = null;
           }
           if ((ratio.X > 0) && (ratio.Y > 0)) this.constrainAspect (rect, ratio, this._resizeHandle); 
        }
        
        this.copyRectangle(this._cropRectangle, rect);
        if (this._showStatusBar) this.updateStatusBar();
        this.drawCropArea();
    },
    
    constrainAspect: function (rect, ratio, handle) {
        
        if ((handle =='N') || (handle == 'S')) {
            var h = rect.Height;
            var calcW = Math.min(Math.max(Math.floor(h * ratio.X / ratio.Y), this._minWidth), this._maxWidth);    
            rect.Width = calcW;
        } else {
            var w = rect.Width;
            var calcH = Math.min(Math.max(Math.floor(w * ratio.Y / ratio.X), this._minHeight), this._maxHeight);       
            if ((handle) && handle.startsWith ('N')) rect.Y = rect.Y - (calcH - rect.Height);
            rect.Height = calcH;
        }   
    },
      
    drawCropArea: function() {
        var px='px';
        var left = this._cropRectangle.X + px;
        var top = this._cropRectangle.Y + px;
        var w = Math.min (this._cropRectangle.Width, Math.max(this._imgW-this._cropRectangle.X,0));
        var h = Math.min (this._cropRectangle.Height, Math.max(this._imgH-this._cropRectangle.Y,0));
        var r = Math.min (this._imgW, (this._cropRectangle.X + w));
        var b = Math.min (this._imgH, (this._cropRectangle.Y + h));
        var width = w + px;
        var height = h + px;
        var right = r + px;
        var bottom = b + px;
        
        var selectedAreaStyle = this._selectedArea.style;
        selectedAreaStyle.left=left;
        selectedAreaStyle.top=top;
        selectedAreaStyle.width=width;
        selectedAreaStyle.height = height;
            
        
        var NSHandleLeft = (Math.ceil((w-6)/2) - 1) + px;
        var EWHandleTop = (Math.ceil((h-6)/2) - 1) + px;
        this._handleN.style.left=NSHandleLeft;
        this._handleE.style.top=EWHandleTop
        this._handleS.style.left=NSHandleLeft;
        this._handleW.style.top=EWHandleTop;
        
        var maskTopStyle = this._maskTop.style;
        maskTopStyle.height=top;
                
        var maskRightStyle=this._maskRight.style;
        maskRightStyle.top=top;
        maskRightStyle.height=height;
        maskRightStyle.left=right;
        maskRightStyle.width=(this._imgW-r) + px;
        
        var maskBottomStyle=this._maskBottom.style;
        maskBottomStyle.top=bottom
        maskBottomStyle.height=(this._imgH-b) + px;
                
        var maskLeftStyle=this._maskLeft.style;
        maskLeftStyle.top=top;
        maskLeftStyle.height=height;
        maskLeftStyle.width=left;
    },
    
    updateStatusBar: function () {
        if (this._statusBar) {
             var s1 = '';
             var s2 = '';
             var s3 = '';
             if (this._showImageSize) s1 = 'Image size: ' + this.sizeToString(this._imageSize);
             if (this._cropRectangle.Width != 0 && this._cropRectangle.Height != 0) {
                var r = this.scaleRectangleToImage(this._cropRectangle);
                s2 = '&nbsp;&nbsp;Crop Area X: ' + r.X + '&nbsp;&nbsp;Y: ' +
                            r.Y + '&nbsp;&nbsp;W: ' + r.Width + '&nbsp;&nbsp;H: ' + r.Height;
             }
//             if (this._processEndTime !=0) {
//                s3 = '&nbsp;&nbsp;Process Time: ' + (this._processEndTime - this._processStartTime) + ' ms';
//                this._processEndTime = 0;
//             }
             this._statusBar.innerHTML = s1 + s2 + s3;
        }   
    },
    
    
    calcGCD: function (a, b) {
        if (b == 0) return a;
        return this.calcGCD(b, a % b);
    },
    
    rectToString: function (rect) {
        if (rect) return '{X: ' + rect.X + ', Y: ' + rect.Y + ', Width: ' + rect.Width + ', Height: ' + rect.Height + '}';
        else return 'null';
    },
    
    pointToString: function (point) {
        if (point) return '{X: ' + point.X + ', Y: ' + point.Y + '}';
        else return 'null';
    },
    
    sizeToString: function (size) {
        if (size) return '{' + size.Width + ' x ' + size.Height + '}';
        else return 'null';
    },
    
    copyRectangle: function (r1, r2) {
        r1.X = r2.X;
        r1.Y = r2.Y;
        r1.Width = r2.Width;
        r1.Height = r2.Height;
    },
    
    cloneRectangle: function (r) {
        return {X: r.X, Y: r.Y, Width: r.Width, Height: r.Height};
    },
    
    scaleRectangleToPreview: function (r) {
        return {X: Math.floor(r.X/this._scale.X), Y: Math.floor(r.Y/this._scale.Y), Width: Math.floor(r.Width/this._scale.X), Height: Math.floor(r.Height/this._scale.Y)};
    },
    
    scaleRectangleToImage: function (r) {
        return {X: Math.floor(r.X*this._scale.X), Y: Math.floor(r.Y*this._scale.Y), Width: Math.floor(r.Width*this._scale.X), Height: Math.floor(r.Height*this._scale.Y)};
    },
    
    waitingMode: function (activated) {
        if (activated) {
          this._animationArea = this.buildElement('DIV', this._prefix + 'AnimationArea');
          this._animationArea.style.top = Math.ceil(this._img.height / 2) + 'px';
          var waitImage = this.buildElement('IMG');
          waitImage.src = this._waitImageUrl;
          this._animationArea.appendChild(waitImage);
          this._frame.insertBefore(this._animationArea, this._frame.firstChild);
        } else {
            if (this._animationArea) {
                 this._frame.removeChild(this._animationArea);
                 this._animationArea = null;
            }
        }
    },
    
    //Property accessors
        
    get_AutoPostBack: function() {
        return this._autoPostBack;
    },
    
    set_AutoPostBack: function (value) {
        this._autoPostBack = value;
    },
    
    get_CallbackID: function() {
        return this._callbackID;
    },
    
    set_CallbackID: function(value) {
        this._callbackID = value;
    },
    
//    get_UseWebService: function() {
//        return this._useWebService;
//    },
//    
//    set_UseWebService: function (value) {
//        this._useWebService = value;
//    },
    
    get_ImageSize : function() {
        if (this._clientState.ImageSize) {
             return this._clientState.ImageSize;
        } else {
             return {Width: 0, Height: 0};
        }
    },

    set_ImageSize : function(value) {
        this._clientState.ImageSize = value;
    },
    
    get_PreviewSize : function () {
        return this._previewSize;
    },
    
    set_PreviewSize : function (value) {
        this._previewSize=value;
    },
    
    get_Scale : function () {
        return this._scale;
    },
    
    set_Scale : function (value) {
        this._scale=value;
    },
    
    get_MinCropSize : function() {
        return this._minCropSize;
    },

    set_MinCropSize : function(value) {
        this._minCropSize=value;
        if ((this._img) && (this._img.complete))this.setMinMaxes();
    },
    
    get_MaxCropSize : function() {
        return this._maxCropSize;
    },

    set_MaxCropSize : function(value) {
        this._maxCropSize=value;
        if ((this._img) && (this._img.complete))this.setMinMaxes();
    },
    
    get_AspectRatio : function() {
        return {Width: this._aspect.X, Height: this._aspect.Y};
    },

    set_AspectRatio : function(value) {
        var x = value.Width;
        var y = value.Height;
        var GCD = this.calcGCD(x, y);
        this._aspect.X=x/GCD;
        this._aspect.Y=y/GCD;
    },
    
    get_AttachCropperOnLoad : function() {
        return this._attachCropperOnLoad;
    },

    set_AttachCropperOnLoad : function(value) {
        this._attachCropperOnLoad = value;
        if (!value) this._showCropperOnInit = false;
    },
    
    get_ShowCropperOnInit : function() {
        return this._showCropperOnInit;
    },

    set_ShowCropperOnInit : function(value) {
        this._showCropperOnInit = value;
    },
    
    get_ShowStatusBar : function() {
        return this._showStatusBar;
    },

    set_ShowStatusBar : function(value) {
         this._showStatusBar = value;
    },
    
    get_ShowImageSize : function() {
        return this._showImageSize;
    },

    set_ShowImageSize : function(value) {
        this._showImageSize = value;
    },
    
    get_CaptureKeys : function() {
        return this._captureKeys;
    },

    set_CaptureKeys : function(value) {
        this._captureKeys = value;
    },
    
    get_ClassNamePrefix : function() {
        return this._prefix;
    },

    set_ClassNamePrefix : function(value) {
        this._prefix = value;
    },
    
    get_WaitImageUrl : function () {
        return this._waitImageUrl;
    },
    
    set_WaitImageUrl : function (value) {
        this._waitImageUrl = value;
    },
    
    get_CacheKeyPrefix : function () {
        return this._cacheKeyPrefix;
    },
    
    set_CacheKeyPrefix : function (value) {
        this._cacheKeyPrefix = value;
    },
    
    get_CropRectangle : function() {
        var rect = {X: 0, Y: 0, Width: 0, Height: 0};
        if (this._clientState.CropRectangle) {
               var r = this._clientState.CropRectangle;
               rect = {X: Math.floor(r.X/this._scale.X), Y: Math.floor(r.Y/this._scale.Y), Width: Math.floor(r.Width/this._scale.X), Height: Math.floor(r.Height/this._scale.Y)};
        }
        return rect;
    },

    set_CropRectangle : function(value) {
        if (value == null) {
           var r = this.scaleRectangleToImage(this._cropRectangle);
           if (r.Width <= 0) r.Width = 1;
           if (r.Height <=0) r.Height = 1;
        } else {
           r = value;
        }
        if (this.get_isInitialized()) {
           var args = [r.X +',' + r.Y + ',' + r.Width + ',' + r.Height];
           this._clientState.CropRectangle = r
           this.raisePropertyChanged("CropRectangle");
           this.raiseEndCrop(args);
           this.processImage('Crop', r);
        }
    },
    
    get_IsDirty : function() {
        if (this._clientState.IsDirty) {
             return this._clientState.IsDirty;
        } else {
             return false;
        }
    },

    set_IsDirty : function(value) {
        if (this.get_isInitialized() && (value != this._clientState.IsDirty)) {
            this._clientState.IsDirty = value;
            if (this._filenameBar) {
                var fileName = this._filenameBar.innerText.replace(/\*$/,'');
                this._filenameBar.innerText = (((fileName.innerText != '') && value) ? fileName + '*' : fileName);
            }
            this.raiseIsDirtyChanged();
        }
    },
    
    add_EndCrop : function (handler) {
        this.get_events().addHandler("EndCrop", handler);
    },
    
    remove_EndCrop : function (handler) {
        this.get_events().removeHandler("EndCrop", handler);
    },
    
    raiseEndCrop : function(args) {
        var onEndCropHandler = this.get_events().getHandler("EndCrop");
        if (onEndCropHandler) {
            onEndCropHandler (this, new WESNet.AJAX.Controls.ImageEditorArgs(args));
        }
    },
    
    get_EndCrop : function() {
        return this.get_Events().getHandler("EndCrop");
    },
    
    set_EndCrop: function (value) {
        if (value && (0<value.length)) {
            var func = CommonToolkitScripts.resolveFunction(value);
            if (func) {
                this.add_EndCrop(func);
            } else {
                throw Error.argumentType('value', typeof(value), 'Function', 'Invalid Handler EndCrop');
            }
        }
    },
    
    add_EndClientCallback: function (handler) {
        this.get_events().addHandler("EndClientCallback", handler);
    },
    
    remove_EndClientCallback: function (handler) {
        this.get_events().removeHandler("EndClientCallback", handler);
    },
    
    raiseEndClientCallback: function (result, context) {
        var handler = this.get_events().getHandler("EndClientCallback");
        if (handler) {
             handler(result, context);
        }
    },
    
    get_EndClientCallback : function() {
        return this.get_Events().getHandler("EndClientCallback");
    },
      
    add_isDirtyChanged: function (handler) {
        this.get_events().addHandler("isDirtyChanged", handler);
    },
    
    remove_isDirtyChanged: function (handler) {
        this.get_events().removeHandler("isDirtyChanged", handler);
    },
    
    raiseIsDirtyChanged: function () {
        var handler = this.get_events().getHandler("isDirtyChanged");
        if (handler) {
             handler(this, Sys.EventArgs.Empty);
        }
    },
    
    processImage: function (operation, r) {
        this._processStartTime = new Date().getTime();
        if (arguments.length==1) {
                r = {X: 0, Y: 0, Width: 0, Height: 0};
            }
        //if (!this._useWebService) { 
            var args = this._imageGuid + '|' + operation + '|' + [r.X +',' + r.Y + ',' + r.Width + ',' + r.Height];
            if (this._autoPostBack) {
               __doPostBack(this._callbackID, args );
            } else {
               this.waitingMode(true);
               __theFormPostData = ''; // force ControlState/ViewState to update
               WebForm_InitCallback(); //  "  "
               WebForm_DoCallback(this._callbackID, args, this.onSuccess, this, this.onError, true);
            }
//        } else {
//            this.waitingMode(true);
//            WESNet.AJAX.Controls.ImageProcessor.ProcessImage(operation, this._imagePath, this.get_CacheKeyPrefix() + this._imageGuid, r, this.onSuccess, this.onError, this);
//        }
    },
    
    /// Error handler for the callback
    onError: function (result, context, methodname) {
        context.waitingMode(false);
        if (methodname) {
             alert ('Error in webservice method ' + methodname + ': ' + result);
        } else {
             alert ('Error in client callback: ' + result);
        }
    },
    
    /// Success handler for the callback
    onSuccess: function (result, context, methodname) {
        context.waitingMode (false);
        if (methodname == null) {
            result = Sys.Serialization.JavaScriptSerializer.deserialize(result);
        }
        
        var img = context._img;
        if (img) {
              var dt = new Date();
              var src = img.src.replace(/&v=\d+/,'&v=' + dt.getMilliseconds());
              img.src = src;
              //context._processEndTime = dt.getTime();
        }
        var imgsize = {Width: result.ImageSize.Width, Height: result.ImageSize.Height}
        context.set_ImageSize(imgsize);
        
        switch (result.Command) {
           case '':
               break;
           case 'Reset':
               context.resetCropper();
               break;
           case 'Remove':
               context.removeCropper();
               break
           case 'Attach':
               context.attachCropper();
        }
        context.raiseEndClientCallback (result, context);
        if (result.IsDirty != context.get_IsDirty()) {
               context.set_IsDirty(result.IsDirty);
        }
    },
    
    loadClientState : function(value) {
        /// <remarks>override this method to intercept client state loading after a callback</remarks>
        if (value) {
              this._clientState = Sys.Serialization.JavaScriptSerializer.deserialize(value);
        } else {
              this._clientState = null;
        }
    },
    saveClientState : function() {
        /// <remarks>override this method to intercept client state acquisition before a callback</remarks>
        if (this._clientState) {
             var value = Sys.Serialization.JavaScriptSerializer.serialize(this._clientState);
             return value;
        } else {
             return null;
        }
    },
    
    mouseOver: function(el) {
       if (el) {
          el.style.borderColor = 'black';
          el.style.borderWidth = '1px';
          el.style.cursor = 'pointer';
       }
    },
    
    mouseOut: function (el) {
       if (el) {
          el.style.borderColor = 'white'
          el.style.borderWidth = '1px';
       }
    },
    
    buildElement: function(tagName, className) {
        var node = document.createElement(tagName);
        node.className=className;
        if (arguments.length>2) {
           for (var i=2; i < arguments.length; i++) {
              var childnodes = arguments[i];
              if (childnodes.length) {
                 for (var j=0; j < childnodes.length; j++) {
                   node.appendChild (childnodes[j]);
                 }
              }
              else {
                 node.appendChild(childnodes);
              }
           }
        }
        return node;
   },
   
   registerHandles: function (enable) {
      if (enable) this._onBeginResizeDelegate=Function.createDelegate(this, this.onBeginResize);
      for (var i=0; i < this._handles.length; i++) {
        var handle=this._handles[i];
        if (enable) {
          var hide=false;
          if (this._isFixedWidth && this._isFixedHeight) hide=true;
            else {
               var isCornerHandle = handle.className.match ( /([S|N][E|W])$/ );
               if (this._isFixedWidth) {
                  var isWidthHandle = handle.className.match ( /(E|W)$/ );
                  if (isCornerHandle || isWidthHandle) hide=true;
               }
               else if (this._isFixedHeight) {
                  var isHeightHandle = handle.className.match ( /(N|S)$/ );
                  if (isCornerHandle || isHeightHandle) hide=true;
               }
          }
          if (hide) handle.style.visibility='hidden';
          else {
            $addHandler (handle, 'mousedown', this._onBeginResizeDelegate);
          }
        }
        else if (handle.style.visibility != 'hidden') {
                  $removeHandler(handle, 'mousedown', this._onBeginResizeDelegate);
             }
     }
  },
  
  getHandle: function (vector) {
      if ((vector) && (this._handles.length > 0)) {
         if ((vector.X == 0) && (vector.Y == 0)) return null;
         var suffix = '';
         if (vector.Y != 0) {
               if (vector.Y <0) suffix = 'N';
               else suffix = 'S';
         }
         if (vector.X != 0) {
               if (vector.X < 0) suffix += 'W';
               else suffix = suffix += 'E';
         }
         var i = 0;
         while (i < this._handles.length) {
            if (this._handles[i].className.endsWith (suffix)) return this._handles[i];
            i++;
         }
     }
     return null;
 }           
}

WESNet.AJAX.Controls.ImageEditorBehavior.registerClass('WESNet.AJAX.Controls.ImageEditorBehavior', WESNet.AJAX.Controls.ControlBase);

WESNet.AJAX.Controls.ImageEditorEventArgs = function (args) {
     WESNet.AJAX.Controls.ImageEditorEventArgs.initializeBase(this);
     this._CropRectangle = args;
}

WESNet.AJAX.Controls.ImageEditorEventArgs.prototype = {
     get_CropRectangle: function () {
             return this._CropRectangle;
     }
}

WESNet.AJAX.Controls.ImageEditorEventArgs.registerClass ('WESNet.AJAX.Controls.ImageEditorEventArgs', Sys.EventArgs);

WESNet.AJAX.Controls.ImageEditorCallbackArgs = function (result, context) {
     WESNet.AJAX.Controls.ImageEditorCallbackArgs.initializeBase(this);
     this._result = result;
     this._context = context
}

WESNet.AJAX.Controls.ImageEditorCallbackArgs.prototype = {
     get_CallbackResult: function () {
             return this._result;
     },
     
     get_Context: function () {
             return this._context;
     }
}

WESNet.AJAX.Controls.ImageEditorCallbackArgs.registerClass ('WESNet.AJAX.Controls.ImageEditorCallbackArgs', Sys.EventArgs);
